package base.thread_demo.demo09;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 在使用jdk提供的ThreadLocal实现线程范围内的数据共享前，我们来复盘下什么叫线程范围内的数据共享案例演示
 *
 * ThreadLocal，可以理解为线程的局部变量，作用就是为每一个使用该变量的线程都提供一个变量值的副本，
 * 每一个线程都可以独立地改变自己的副本，而不会和其它线程的副本冲突。
 * ThreadLocal和Synchonized区别：
 * 都用于解决多线程并发访问。
 * Synchronized用于线程间的数据共享（使变量或代码块在某一时该只能被一个线程访问），是一种以延长访问时间来换取线程安全性的策略；
 * 而ThreadLocal则用于线程间的数据隔离（为每一个线程都提供了变量的副本），是一种以空间来换取线程安全性的策略。
 *
 * 两个线程：相当于两笔转账业务，每个线程上需要同时执行A、B两个模块。该业务属于并发操作
 * A、B两个模块：分别代指转入、转出业务逻辑。A、B两个业务模块获取的数据要保持一致性，因为A、B属于同一个线程上的两个步骤，因此存在
 * 同一线程上数据共享问题。
 * A、B两个业务能维持数据共享关键实现是：线程在执行A/B时，可以获取到线程名，共享数据Map中可以根据线程名这个key来获取对应线程数据。
 *
 * 线程范围内数据共享典型应用就是数据库事务控制：
 * A好比从账户扣钱，转往B代表的往账户存钱，如果只进行了A步骤，中间出现意外，要保证B步骤没成功，A要能够回退。
 * 数据库中，开始事务和结束事务属于同一线程，开始事务上的connection对象和结束事务上的connection对象是同一个connection对象。
 * 每个事务上有各自的connection对象，提交事务操作的是各自的connection对象。
 *
 */
public class ThreadScopeShareData {
    private static final Logger log = LoggerFactory.getLogger(ThreadScopeShareData.class);

    // 通过Map封装线程范围内共享变量演示
    private static Map<Thread, Integer> map = new HashMap<>();

    public static void main(String[] args) {
        // 通过两个线程，分别给Map中塞两条跟线程相关的数据（用线程对象作为key）
        new Thread(()->{
            int data = new Random().nextInt();
            // 模拟通过线程"t1"给账户key存了点钱
            map.put(Thread.currentThread(), data);
            log.info("{} has put data :{}", Thread.currentThread().getName(), data);

            // 模拟A模块从线程"t1"中获取数据
            new ThreadScopeShareData().new A().get();
            // 模拟B模块从线程"t1"中获取数据
            new B().get();

        },"t1").start();

        new Thread(()->{
            int data = new Random().nextInt();
            map.put(Thread.currentThread(), data);
            log.info("{} has put data :{}", Thread.currentThread().getName(), data);

            // A,B 模块输出相等就说明实现了线程内部共享了数据
            new ThreadScopeShareData().new A().get();
            new B().get();
        },"t2").start();
    }

    // A 模块（模拟转入）
    class A{
        private final Logger log = LoggerFactory.getLogger(A.class);
        public void get(){
            Integer data = map.get(Thread.currentThread());
            log.info("{}线程*****A: {}", Thread.currentThread().getName(), data);
        }
    }
    // B 模块（模拟转出）
    static class B{
        private final Logger log = LoggerFactory.getLogger(B.class);
        public void get(){
            Integer data = map.get(Thread.currentThread());
            log.info("{}线程*****B: {}", Thread.currentThread().getName(), data);
        }
    }
}

